#include "StdAfx.h"
#include "CommandParse.h"
#include "PacketProcess.h"


///command info
struct TimerCommand
{
	tm _tm;
	int server;
	int times;
	int interval;
	int delay;
	bool local;
	char context[MAX_GM_CMD_LEN + 1];
	struct TimerCommand* next;

	TimerCommand()
	{
		__time64_t _time;
		_time64(&_time);
		localtime_s( &_tm, &_time );

		server = 0;
		times = 1;
		interval = 1;
		delay = 0;
		local = true;
		memset( context, 0x0, sizeof(context) );

		next = NULL;
	}
};

static TimerCommand* pTimerCommand = NULL;
static TimerCommand* pTimerCommandHead = NULL;

TimerCommand* GetTimerCommand()
{
	if( pTimerCommand == NULL )
	{
		pTimerCommand = new TimerCommand;
	}

	return pTimerCommand;
}


///command & function pair
int CmdHelp( LPCSTR pszParams );
int CmdSetDate( LPCSTR pszParams );
int CmdSetMoment( LPCSTR pszParams );
int CmdSetTimes( LPCSTR pszParams );
int CmdSetInterval( LPCSTR pszParams );
int CmdSetDelay( LPCSTR pszParams );
int CmdSetServer( LPCSTR pszParams );
int CmdSetLocation( LPCSTR pszParams );
int CmdSetCommand( LPCSTR pszParams );

struct Command
{
	LPCSTR cmd;
	LPCSTR info;
	int (*fn)( LPCSTR pszParams );
};

static Command CommandTable[] = 
{
	{ "date", "set command execute date yyyy-mm-dd", CmdSetDate },
	{ "moment", "set command execute moment hh:mm:ss", CmdSetMoment },
	{ "interval", "set command execute interval", CmdSetInterval },
	{ "times", "set command execute times", CmdSetTimes },
	{ "delay", "set command delay", CmdSetDelay },
	{ "server", "set command server", CmdSetServer },
	{ "location", "set command local/server", CmdSetLocation },
	{ "command", "set execute command line", CmdSetCommand },
};


///command parse function
typedef std::vector<std::pair<int,std::string>> ParamVector;
int SeplitParams( LPCSTR pszParams, ParamVector &vec, int n = -1 )
{
	CHAR* next = NULL;
	CHAR* string = _strdup( pszParams );
	CHAR* tok = strtok_s( string, " ,;-:", &next );

	size_t nLength = 0;
	for( int i = 0; tok != NULL && i != n; ++i, tok = strtok_s( NULL, " ,;-:", &next ) )
	{
		vec.push_back( std::make_pair( int(tok - string), tok ) );
		nLength = next - string;
	}

	free( string );

	return nLength;
}

int CmdSetDate( LPCSTR pszParams )
{
	TimerCommand* mTimerCommand = GetTimerCommand();

	ParamVector vec;
	int nRead = SeplitParams(pszParams, vec, 3);
	if( vec.size() != 3 || mTimerCommand == NULL )
		return nRead;

	mTimerCommand->_tm.tm_year	= atoi(vec[0].second.c_str()) - 1900;
	mTimerCommand->_tm.tm_mon	= atoi(vec[1].second.c_str()) - 1;
	mTimerCommand->_tm.tm_mday	= atoi(vec[2].second.c_str());

	return nRead;
}

int CmdSetMoment( LPCSTR pszParams )
{
	TimerCommand* mTimerCommand = GetTimerCommand();

	ParamVector vec;
	int nRead = SeplitParams(pszParams, vec, 3);
	if( vec.size() != 3 || mTimerCommand == NULL )
		return nRead;

	mTimerCommand->_tm.tm_hour	= atoi(vec[0].second.c_str());
	mTimerCommand->_tm.tm_min	= atoi(vec[1].second.c_str());
	mTimerCommand->_tm.tm_sec	= atoi(vec[2].second.c_str());

	return nRead;
}

int CmdSetTimes( LPCSTR pszParams )
{
	TimerCommand* mTimerCommand = GetTimerCommand();

	ParamVector vec;
	int nRead = SeplitParams(pszParams, vec, 1);
	if( vec.size() != 1 || mTimerCommand == NULL )
		return nRead;

	mTimerCommand->times	= atoi(vec[0].second.c_str());

	return nRead;
}

int CmdSetInterval( LPCSTR pszParams )
{
	TimerCommand* mTimerCommand = GetTimerCommand();

	ParamVector vec;
	int nRead = SeplitParams(pszParams, vec, 1);
	if( vec.size() != 1 || mTimerCommand == NULL )
		return nRead;

	mTimerCommand->interval	= atoi(vec[0].second.c_str());

	return nRead;
}

int CmdSetDelay( LPCSTR pszParams )
{
	TimerCommand* mTimerCommand = GetTimerCommand();

	ParamVector vec;
	int nRead = SeplitParams(pszParams, vec, 1);
	if( vec.size() != 1 || mTimerCommand == NULL )
		return nRead;

	mTimerCommand->delay	= atoi(vec[0].second.c_str());

	return nRead;
}

int CmdSetCommand( LPCSTR pszParams )
{
	TimerCommand* mTimerCommand = GetTimerCommand();

	if( mTimerCommand != NULL )
	{
		ParamVector vec;
		SeplitParams(pszParams, vec, 1 );

		if( vec.size() > 0 && *(pszParams + vec[0].first) == '/' )
		{
			return _snprintf_s( mTimerCommand->context, sizeof(mTimerCommand->context), _countof(mTimerCommand->context), pszParams + vec[0].first ) + vec[0].first;
		}
	}

	return -1;
}

int CmdSetServer( LPCSTR pszParams )
{
	TimerCommand* mTimerCommand = GetTimerCommand();

	ParamVector vec;
	int nRead = SeplitParams(pszParams, vec, 1);
	if( vec.size() != 1 || mTimerCommand == NULL )
		return nRead;

	mTimerCommand->server	= atoi(vec[0].second.c_str());

	return nRead;
}

int CmdSetLocation( LPCSTR pszParams )
{
	TimerCommand* mTimerCommand = GetTimerCommand();

	ParamVector vec;
	int nRead = SeplitParams(pszParams, vec, 1);
	if( vec.size() != 1 || mTimerCommand == NULL )
		return nRead;

	if ( vec[0].second.compare( "server" ) == 0 )
	{
		mTimerCommand->local = false;
	}
	else
	{
		mTimerCommand->local = true;
	}

	return nRead;
}


void SendCommand( LPCSTR pszParams, INT nServerNum = 0 )
{
	TimerCommand* pCommand = pTimerCommand;
	if ( pCommand == NULL )
	{
		TimerCommand tempCommand;
		pCommand = &tempCommand;
	}

	int nDataLen = sizeof(MessageHeader) + sizeof(SGM2C_COMMAND);
	xgc_byte* pData = new xgc_byte[nDataLen];
	memset( pData, 0x0, nDataLen );

	MessageHeader* pHead = (MessageHeader*)pData;
	pHead->seq = 0;
	pHead->cmd = GM2C_COMMAND;
	pHead->length = nDataLen;

	SGM2C_COMMAND* pMsg = (SGM2C_COMMAND*)(pData + sizeof(MessageHeader));
	int nTime = (int)mktime( &(pCommand->_tm) );
	pMsg->nMoment = nTime + pCommand->delay;
	pMsg->nServer = pCommand->server;
	pMsg->nTimes = pCommand->times;
	pMsg->nInterval = pCommand->interval;
	memcpy_s( pMsg->szCommand, sizeof(pMsg->szCommand), pszParams, strlen(pszParams) );

	net::SendPacket( g_netHandle, pData, nDataLen );
	delete[] pData;
}

int ParseCommand( LPCSTR pszParams, INT nServerNum )
{
	if( pszParams[0] == '/' )
	{
		__time64_t _now;
		_time64(&_now);
		tm _tm;
		localtime_s( &_tm, &_now );

		printf( "%4d-%02d-%02d %02d:%02d:%02d : execute \"%s\"\n", _tm.tm_year + 1900, _tm.tm_mon+1, _tm.tm_mday, _tm.tm_hour, _tm.tm_min, _tm.tm_sec, pszParams );
		SendCommand( pszParams, nServerNum );

		return 0;
	}

	int nProcess = 0, nRead = 0;
	while( pszParams[nProcess] != 0 )
	{
		LPCSTR p = strchr( pszParams+nProcess, ' ' );
		if( p == NULL ) 
			p = pszParams+nProcess+strlen(pszParams+nProcess);

		for( int i = 0; i < _countof(CommandTable); ++i )
		{
			if( _tcsnicmp( pszParams+nProcess, CommandTable[i].cmd, p - (pszParams+nProcess) ) == 0 )
			{
				nProcess = p - pszParams;
				nRead = CommandTable[i].fn( p );
				break;
			}
		}
		
		if( nRead == 0 )
			break;

		if( nRead < 0 )
		{
			if( pTimerCommand )
			{
				delete pTimerCommand;
				pTimerCommand = NULL;
			}
			break;
		}

		nProcess += nRead;
		nRead = 0;
	}

	if( pTimerCommand && pszParams[nProcess] == 0)
	{
		pTimerCommand->next = pTimerCommandHead;
		pTimerCommandHead = pTimerCommand;
	}
	else
	{
		delete pTimerCommand;
		printf( "error command line...\n" );
	}

	pTimerCommand = NULL;
	return 0;
}

void ExcuteCommand()
{
	__time64_t _now;
	_time64(&_now);
	tm _tm;
	localtime_s( &_tm, &_now );

	pTimerCommand = pTimerCommandHead;
	while( pTimerCommand )
	{
		_tm = pTimerCommand->_tm;
		_tm.tm_sec += pTimerCommand->delay;
		__time64_t _time = _mktime64( &_tm );

		if( _time != -1 && _time <= _now )
		{
			ParseCommand( pTimerCommand->context, pTimerCommand->server );
			pTimerCommand->times--;
			pTimerCommand->_tm.tm_sec += pTimerCommand->interval;
		}

		pTimerCommand = pTimerCommand->next;
	}

	// delete zero times item
	pTimerCommand = pTimerCommandHead;

	// head
	while( pTimerCommand && ( !pTimerCommand->local || pTimerCommand->times <= 0 ) )
	{
		pTimerCommandHead = pTimerCommand->next;
		delete pTimerCommand;
		pTimerCommand = pTimerCommandHead;
	}

	// other
	while( pTimerCommand && pTimerCommand->next )
	{
		if( !pTimerCommand->next->local || pTimerCommand->next->times <= 0 )
		{
			TimerCommand* tmpTimerCommand = pTimerCommand->next;
			pTimerCommand->next = pTimerCommand->next->next;
			delete tmpTimerCommand;
		}
		else
		{
			pTimerCommand = pTimerCommand->next;
		}
	}

	pTimerCommand = NULL;
}

void ShowCmdInfo()
{
	for( int i = 0; i < _countof(CommandTable); ++i )
	{
		printf( "%s : %s\n", CommandTable[i].cmd, CommandTable[i].info );
	}
}
